Skip to content

Document MCP server generator - #6778

Draft
devalog wants to merge 13 commits into
mainfrom
devalog/mcp-gen-docs
Draft

Document MCP server generator#6778
devalog wants to merge 13 commits into
mainfrom
devalog/mcp-gen-docs

Conversation

@devalog

@devalog devalog commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

</Indent>

<ParamField path="include" type="array of objects" required={false} toc={true}>
Endpoints to expose as tools. Entries are OR'd together; fields within a single entry are AND'd. Omit `include` to start from every endpoint the spec exposes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] <FernStyles.Acronyms> reported by reviewdog 🐶
'AND' has no definition.

- An `include`/`exclude` selector that matches nothing.
- A destructive endpoint with no `destructive` annotation override.
- A new spec endpoint matching no configured group.
- An **orphaned override** — an `overrides` entry keyed to an endpoint no longer in the spec, so a hand-written description doesn't just silently stop applying.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] <FernStyles.Hedges> reported by reviewdog 🐶
Avoid hedge words and filler like 'just'. Prefer direct statements.

- An `include`/`exclude` selector that matches nothing.
- A destructive endpoint with no `destructive` annotation override.
- A new spec endpoint matching no configured group.
- An **orphaned override** — an `overrides` entry keyed to an endpoint no longer in the spec, so a hand-written description doesn't just silently stop applying.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] <FernStyles.Adverbs> reported by reviewdog 🐶
Remove 'silently' if it's not important to the meaning of the statement.

Suggested change
- An **orphaned override** — an `overrides` entry keyed to an endpoint no longer in the spec, so a hand-written description doesn't just silently stop applying.
- An **orphaned override** — an `overrides` entry keyed to an endpoint no longer in the spec, so a hand-written description doesn't just stop applying.

- A `server-name` or package rename on an already-published group, which `tools --diff` can't see on its own since it compares tools, not identity.
- A `response-fields` projection set under a generator version that doesn't support it.

All of these print as warnings, not blockers, in CI and `--json` output.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] <Microsoft.Wordiness> reported by reviewdog 🐶
Consider using 'these' instead of 'All of these'.

Suggested change
All of these print as warnings, not blockers, in CI and `--json` output.
these print as warnings, not blockers, in CI and `--json` output.


`fern mcp init` always adds a new group; it never mutates an existing one. `fern mcp tools --refine --group <name>` is the mutation path for a group already written.

Two endpoints can appear in more than one group's toolset — `fern mcp list` reports the overlap as information, not a warning. When a spec grows a new endpoint, it lands in whichever group's rules match; `fern check` warns if a new endpoint matches none of them, so nothing silently falls through the cracks.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] <FernStyles.Adverbs> reported by reviewdog 🐶
Remove 'silently' if it's not important to the meaning of the statement.

Suggested change
Two endpoints can appear in more than one group's toolset — `fern mcp list` reports the overlap as information, not a warning. When a spec grows a new endpoint, it lands in whichever group's rules match; `fern check` warns if a new endpoint matches none of them, so nothing silently falls through the cracks.
Two endpoints can appear in more than one group's toolset — `fern mcp list` reports the overlap as information, not a warning. When a spec grows a new endpoint, it lands in whichever group's rules match; `fern check` warns if a new endpoint matches none of them, so nothing falls through the cracks.

- A **group** is a separately generated and published server — its own npm package, its own GitHub repo, its own version.
- A **preset** is a named subset of one group's toolset that a client can connect to directly — one deploy, several audiences.

Reach for multiple groups when the audiences need different deployments (an internal admin server your public one shouldn't ship). Reach for presets when one deployment is enough and you just want to hand different clients a narrower slice of it. Small APIs typically need neither; large ones mostly need presets before they need multiple groups.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] <FernStyles.Hedges> reported by reviewdog 🐶
Avoid hedge words and filler like 'just'. Prefer direct statements.

The MCP generator is in early access. [Reach out](https://buildwithfern.com/book-demo?type=mcp) to get started.
</Note>

Fern's MCP generator turns your API definition into a [Model Context Protocol](https://modelcontextprotocol.io) server: a project where every endpoint becomes a typed tool that Claude, Cursor, and other MCP clients can call directly. It shares the same spec, `generators.yml`, and generation pipeline as your SDKs and CLI — an MCP server is just another generator output, configured as a [group](/learn/mcp-generator/get-started/quickstart) like any other.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] <FernStyles.Hedges> reported by reviewdog 🐶
Avoid hedge words and filler like 'just'. Prefer direct statements.

availability: beta
---

An MCP server that exposes 300 tools is worse than one that exposes 30: agents spend context distinguishing similar-sounding tools, take more turns to find the right one, and are more likely to call something destructive by accident. Curating the toolset — not just generating one — is the part of setting up an MCP server that an SDK never needed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] <FernStyles.Hedges> reported by reviewdog 🐶
Avoid hedge words and filler like 'just'. Prefer direct statements.


Narrowing writes compound selectors where they express the intent — `{ tag: reports, method: GET }` for "read-only reports" — rather than an enumerated endpoint list. Response-field projection keeps a tool but restricts its response to the fields you name, via `overrides.<endpoint>.response-fields` in the [configuration reference](/learn/mcp-generator/get-started/configuration#overrides). "Switch to dynamic mode" writes `tools.mode: dynamic` as a stated trade-off, not a silent one.

`--refine` mutates an existing group's config in place. `fern mcp init` always adds a new group and never mutates one — the two commands are deliberately disjoint.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] <FernStyles.Adverbs> reported by reviewdog 🐶
Remove 'deliberately' if it's not important to the meaning of the statement.

Suggested change
`--refine` mutates an existing group's config in place. `fern mcp init` always adds a new group and never mutates one — the two commands are deliberately disjoint.
`--refine` mutates an existing group's config in place. `fern mcp init` always adds a new group and never mutates one — the two commands are disjoint.

◆ Accept / Adjust (describe the change) / Start over / Switch to manual
```

The proposal leads with what it excluded, since a 20-tool include list is hard to eyeball but an exclusion list is easy to verify against what you asked for. Anything the model can't express as a selector — a spending limit, an escalation rule — is written into the server's `instructions` field or a per-tool `overrides` description instead of silently dropped. The output is always the same declarative rules any other preset writes: reviewable, diffable, and safe to hand-edit afterward. Run it non-interactively with `fern mcp init --preset ai --intent "support agents, refunds under $100, no admin"`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [vale] <FernStyles.Adverbs> reported by reviewdog 🐶
Remove 'silently' if it's not important to the meaning of the statement.

Suggested change
The proposal leads with what it excluded, since a 20-tool include list is hard to eyeball but an exclusion list is easy to verify against what you asked for. Anything the model can't express as a selector — a spending limit, an escalation rule — is written into the server's `instructions` field or a per-tool `overrides` description instead of silently dropped. The output is always the same declarative rules any other preset writes: reviewable, diffable, and safe to hand-edit afterward. Run it non-interactively with `fern mcp init --preset ai --intent "support agents, refunds under $100, no admin"`.
The proposal leads with what it excluded, since a 20-tool include list is hard to eyeball but an exclusion list is easy to verify against what you asked for. Anything the model can't express as a selector — a spending limit, an escalation rule — is written into the server's `instructions` field or a per-tool `overrides` description instead of dropped. The output is always the same declarative rules any other preset writes: reviewable, diffable, and safe to hand-edit afterward. Run it non-interactively with `fern mcp init --preset ai --intent "support agents, refunds under $100, no admin"`.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Review: #6778 (MCP Generator docs) vs. the linked implementation PRs

Scope of what could be verified:

  • fern-platform PRs (#14253, #14402, #14355) — all merged into app, reviewed against the fern-platform checkout on this machine.
  • postman-eng/sdk-gen-core PRs (docs: Add intro text and fix trailing newline in SDK guide #2165, Update changelogs from fern repo #2183) — not accessible (403 from the git proxy; the repo isn't in Devin's repo list). These almost certainly contain the generator implementation itself, so generator-internal claims (env-var derivation, MCP_TOOLSET, metadata.json, npm run setup, dist/index.js, annotation emission) could not be verified.
  • The fern mcp CLI surface documented in the PR lives on the unmerged prototype branch devin/1787772708-mcp-cli-prototype in fern-api/fern (nothing is on main except fern mcp install, which is a different feature, plus sdk-gen-api routing for fernapi/fern-mcp-server at version 0.1.0). The docs were checked against that branch as the closest source of truth.

What checks out

  • Generator name fernapi/fern-mcp-server and version: 0.1.0 — registered in generatorConfigPolicy.ts on fern main.
  • fern mcp init exists with --name, --preset, --group, -y/--yes, --json, --api; -y does accept the read-only preset non-interactively.
  • Presets: read-only (all GETs + detected read-like POSTs, ambiguous ones excluded with a note to run fern mcp tools), main-resources (one include per primary tag, disabled when tags are unusable), AI-curated, everything.
  • Budget verdict: green ≤40 tools / ≤60k tokens, amber up to 3×, red beyond — matches the documented table and the budget defaults.
  • Selector schema: tag, method, operation-id (glob), path-prefix, endpoint — ANDed within an entry, ORed across entries, exclude wins, omitted include = all endpoints.
  • server-name default: kebab-cased API title with -mcp appended.
  • tools.presets exists; fern mcp tools --preset resolves a named preset.
  • "No tags → grouping by path prefix" diagnosis banner, and refine offering path prefixes on tagless specs.
  • Docs mechanics: nav registration, availability: beta, the <Prompt> component, the init-fern-folder snippet, and all internal /learn/mcp-generator/get-started/* links resolve correctly (the CI preview confirms the URLs). Links to /learn/sdks/reference/generators-yml and /learn/docs/fern-agent are correct.

Discrepancies vs. the implementation (prototype branch)

Commands that don't exist as documented

  1. fern mcp install --local --group mcp (local-development.mdx) — no such subcommand or flags. Worse, fern mcp install already exists on main with different semantics (connects coding agents to Fern's own hosted MCP server via URL + bearer token) and is being renamed to fern agent install on branch devin/1788209320-fern-agent-install. The docs' usage collides with the existing/renamed command.
  2. fern mcp tools --diff and the whole tools.lock breaking-change story (maintaining.mdx) — --diff isn't implemented; tools.lock is written only by the stub fern mcp generate and nothing reads it. No additive/breaking classification, no fern check warning, no version-bump recommendation.
  3. fern mcp init --dry-run — no such flag.
  4. fern mcp init --preset ai --intent "…" (tool-selection.mdx) — invalid: --preset only accepts read-only, main-resources, everything. The AI-curated path is triggered by --intent without --preset.
  5. fern mcp tools --refine --ai (configuration.mdx intent field) — no --ai flag on tools.

Config fields not implemented in the CLI

  1. tools.mode (static/dynamic meta-tools)ToolsConfig has no mode field; nothing implements the list/describe/invoke meta-tools. Documented in configuration.mdx, tool-selection.mdx (a whole paragraph), and the refine transcript.
  2. tools.budget (max-tools/max-tokens) — the budget is a hardcoded constant (40 / 60k); computeVerdict accepts a budget but no config parsing reads one. The docs' "The thresholds are configurable per group" is unimplemented.
  3. tools.overrides (name, description, read-only, destructive, response-fields, deprecated) — parseToolsConfig ignores overrides entirely. May still be a generator-side feature (unverifiable without sdk-gen-core), but every documented CLI behavior around it — orphaned-override warnings, "the Refine loop writes response-fields", deprecated: true handling — is absent.

Behavioral gaps

  1. fern mcp dev is documented as building the server, attaching the MCP inspector, and printing export commands for missing credentials. The implementation prints static instructions only ("this prototype does not start a real MCP runtime") and references server.js, while the docs claim dist/index.js.
  2. fern mcp list — docs claim it reports "toolset overlaps with other groups"; it prints api/group/server/presets/output/count/tokens only.
  3. fern mcp tools — docs claim a quality lint (junk-derived names, collisions, near-duplicates, oversized responses) and a "highest-cost tools with a reason" listing; implementation prints a tool table + verdict only.
  4. Refine loop — docs' transcript offers "Narrow rules… / Filter oversized responses (project fields)… / Switch to dynamic mode / Accept as-is" after listing the highest-cost tools. Implementation offers only "Remove endpoints by tag/method/path-prefix" + "Keep as-is" (excludes only, never adds projection or mode changes).
  5. fern check rules — the entire "fern check rules" section (budget warning, empty selectors, missing destructive annotation, uncovered new endpoints, orphaned overrides, published-group renames, unsupported response-fields) is unimplemented; fern check has no MCP-specific logic.
  6. AI-curated — the implementation is an explicitly stubbed local pattern-matcher ("The real implementation is server-side (FAI)"). It does not require fern login (no auth check exists in the prototype), does not write instructions, and does not propose per-audience group splits (multiple-servers.mdx's "This API serves 3 distinct audiences" flow is unimplemented).
  7. Generated output pathfern mcp init writes output.path: ../../generated/<group>; the quickstart shows ../generated/mcp. Either the docs or the prototype is off by a directory level (worth checking which is intended — ../../ escapes the repo root if the path is relative to fern/).
  8. fern mcp init "never mutates" — docs claim init always adds a new group and never mutates one. writeMcpGroupToGeneratorsYml will silently replace an existing fern-mcp-server entry in the named group, so fern mcp init --group existing does mutate.

Framing issue vs. the platform PRs

  1. Hosting is never mentioned, and overview.mdx says the opposite of what shipped. The overview states the output "runs over stdio … so there's nothing to host or expose a port for." But fern-platform #14253 + #14355 (both merged) ship exactly that: fern generate → a hosted MCP server at https://{org}.fernmcp.dev/{slug}, with a dispatcher worker, an outbound-credential-confinement worker, and a browser connect page. If the docs are meant to track the shipped feature set, there's a missing "hosted/publishing" path (or at least the "nothing to host" sentence needs a caveat); if hosting docs are deliberately deferred, the sentence will still be wrong the moment the CLI publish step lands. #14402 (env-var wiring) is internal-only and rightly not documented.

Minor / docs-side notes

  • The dynamic mode link in configuration.mdx points to tool-selection#presets — the anchor resolves, but it links the phrase "dynamic mode" to a section titled "Presets"; fine, though a dedicated heading/anchor would be cleaner.
  • Docs convention compliance is otherwise good: sentence-case headings, new-product nav entry mirrors the CLI Generator product, placeholder icon/image is already flagged with an inline TODO in docs.yml, no files renamed so no redirects needed. A changelog entry is not strictly required (the rule covers the Docs product; this is a new product) but an announcement entry might still be worth considering.

Bottom line

The documented skeleton (generator identity, group wiring, presets, budget thresholds, selector semantics, env-var naming conventions) matches the design intent visible in the prototype. But a large share of the documented surface — overrides, mode, budget, --diff/tools.lock, install --local, the fern check rules, the inspector/credential-detection behavior of dev, and the AI-curated extras — is either unimplemented in the only CLI implementation available (the prototype branch) or lives in sdk-gen-core, which I cannot access. The PR is a draft and the feature is early-access/prototype, so documenting ahead may be intentional — but if the intent was "document what exists," roughly a third of the documented flags/config/commands describe features that don't exist yet.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

devin-ai-integration Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Comment thread fern/products/mcp-generator/preview/overview.mdx
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Comment thread fern/products/mcp-generator/preview/overview.mdx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants